package com.tuniu.agents.common.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.ai.tool.resolution.ToolCallbackResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Map;

@RestController
@RequestMapping("tools")
@Profile({"sit","dev"})
public class ToolController {

    @Autowired
    private ToolCallbackResolver toolCallbackResolver;

    @Autowired
    private ObjectMapper objectMapper;

    @SneakyThrows
    @GetMapping("/openapi/{toolNames}")
    public ResponseEntity<String> openapi(@PathVariable("toolNames") String toolNames) {
        String[] toolArray = StringUtils.split(toolNames, ",");
        if (toolArray == null || toolArray.length == 0) {
            return ResponseEntity.badRequest().body("No valid tool names provided.");
        }

        ObjectNode openApiDoc = objectMapper.createObjectNode();
        openApiDoc.put("openapi", "3.1.0");
        ObjectNode infoNode = objectMapper.createObjectNode();
        openApiDoc.set("info", infoNode);

        ObjectNode serverNode = objectMapper.createObjectNode();
        serverNode.put("url", getCurrentBaseUrl());
        openApiDoc.set("servers", objectMapper.createArrayNode().add(serverNode));

        ObjectNode pathsNode = objectMapper.createObjectNode();
        for (String toolName : toolArray) {
            FunctionCallback functionCallback = toolCallbackResolver.resolve(toolName);
            String functionName = functionCallback.getName();
            String description = functionCallback.getDescription();
            JsonNode inputNode = objectMapper.readTree(functionCallback.getInputTypeSchema());

            ObjectNode toolPathNode = createToolPath(functionName, description, inputNode);
            mergeNodes(pathsNode, toolPathNode);
        }

        openApiDoc.set("paths", pathsNode);
        openApiDoc.set("components", objectMapper.createObjectNode().set("schemas", objectMapper.createObjectNode()));

        return ResponseEntity.ok(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(openApiDoc));
    }

    @PostMapping("/call/{toolName}")
    public ResponseEntity<?> callTool(@PathVariable("toolName") String toolName, @RequestBody String jsonObject) {
        FunctionCallback functionCallback = toolCallbackResolver.resolve(toolName);
        return ResponseEntity.ok(functionCallback.call(jsonObject, new ToolContext(Map.of())));
    }

    private ObjectNode createToolPath(String functionName, String description, JsonNode inputNode) {
        ObjectNode pathsNode = objectMapper.createObjectNode();
        ObjectNode pathDetailsNode = objectMapper.createObjectNode();
        pathDetailsNode.put("description", description);
        pathDetailsNode.put("operationId", functionName);
        pathDetailsNode.put("deprecated", false);

        ObjectNode requestBodyNode = objectMapper.createObjectNode();
        requestBodyNode.put("description", "Query parameters for " + functionName);
        requestBodyNode.put("required", true);

        ObjectNode contentNode = objectMapper.createObjectNode();
        contentNode.set("application/json", objectMapper.createObjectNode().set("schema", inputNode));

        requestBodyNode.set("content", contentNode);
        pathDetailsNode.set("requestBody", requestBodyNode);

        pathsNode.set("/tools/call/" + functionName, objectMapper.createObjectNode().set("post", pathDetailsNode));

        return pathsNode;
    }

    private void mergeNodes(ObjectNode mainNode, ObjectNode newNode) {
        newNode.fields().forEachRemaining(entry -> {
            String fieldName = entry.getKey();
            JsonNode value = entry.getValue();
            mainNode.set(fieldName, value);
        });
    }

    private String getCurrentBaseUrl() {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attrs.getRequest();

        StringBuilder url = new StringBuilder();
        url.append(request.getScheme()) // http or https
                .append("://")
                .append(request.getServerName()); // domain or IP address

        if (request.getServerPort() != 80 && request.getServerPort() != 443) {
            url.append(":").append(request.getServerPort());
        }

        return url.toString();
    }

}
